package com.ytzl.itrip.service.impl;

import com.alibaba.fastjson.JSON;
import com.ytzl.itrip.common.DigestUtil;
import com.ytzl.itrip.common.RedisAPI;
import com.ytzl.itrip.common.UserAgentUtil;
import com.ytzl.itrip.exception.TokenValidationFailedException;
import com.ytzl.itrip.pojo.ItripUser;
import com.ytzl.itrip.service.TokenService;
import cz.mallat.uasparser.UserAgentInfo;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.io.IOException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;

/**
 * Created by sam on 2018/2/8.
 */
@Service("tokenService")
public class TokenServiceImpl extends BaseService implements TokenService {

    @Resource
    protected RedisAPI redisAPI;
    //过期时间，两个小时
    private int exprie = SESSION_TIMEOUT;

    @Override
    public String generateToken(String agent, ItripUser itripUser) {
        try {
            UserAgentInfo agentInfo = UserAgentUtil.getUasParser().parse(agent);
            StringBuffer sb = new StringBuffer();
            //拼接统一前缀
            sb.append(PREFIX_TOKEN);
            //判断是否知道设备类型
            if (agentInfo.getDeviceType().equals(UserAgentInfo.UNKNOWN))
                //如果不知道,直接判断是否是移动设备
                if (UserAgentUtil.CheckAgent(agent))
                    //如果是
                    sb.append("MOBILE-");
                else
                    sb.append("PC-");
            else if (agentInfo.getDeviceType().equals("Personal computer"))
                sb.append("PC-");
            else
                sb.append("MOBILE-");
            //用户Code
            sb.append(DigestUtil.digest(itripUser.getUserCode()) + "-");//加密用户code
            //ID
            sb.append(itripUser.getId() + "-");
            //拼接token创建时间
            sb.append(new SimpleDateFormat("yyyyMMddHHmmss").format(new Date()) + "-");
            // 识别客户端的简化实现——6位MD5码
            sb.append(DigestUtil.digest(agent).substring(0, 6));
            return sb.toString();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    @Override
    public void save(String token, ItripUser itripUser) {
        //PC端认证需要有有效期，移动端永久不失效
        if (token.startsWith(PREFIX_TOKEN + "PC:")) {
            redisAPI.set(token, exprie, JSON.toJSONString(itripUser));
        } else {
            redisAPI.set(token, JSON.toJSONString(itripUser));//移动端永不过期
        }
    }

    public boolean exists(String token) {
        return redisAPI.exist(token);
    }


    @Override
    public boolean validate(String userAgent, String token) {
        //判断token 是否存在
        if (!exists(token))
            return false;
        //效验token是否过期
        String[] split = token.split("-");
        try {
            //获取创建Token的时间
            Date createTime = new SimpleDateFormat("yyyyMMddHHmmss").parse(split[3]);
            //获取时间差
            long passed = Calendar.getInstance().getTimeInMillis() - createTime.getTime();
            if (passed > SESSION_TIMEOUT * 1000)
                return false;
            //判断浏览器标识
            if (DigestUtil.digest(userAgent).substring(0, 6).equals(split[4]))
                return true;
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return false;
    }

    @Override
    public void remove(String token) {
        if (exists(token))
            redisAPI.delete(token);
    }

    @Override
    public String replaceToken(String userAgent, String token) throws TokenValidationFailedException {
        //判断Token是否存在
        if (!exists(token))
            throw new TokenValidationFailedException("未知的Token或Token已过期");
        //获取Token的生成时间，转换为Date
        Date createTime = null;
        String[] split = token.split("-");
        try {
            createTime = new SimpleDateFormat("yyyyMMddHHmmss").parse(split[3]);
        } catch (ParseException e) {
            e.printStackTrace();
            throw new TokenValidationFailedException("token格式异常");
        }
        //Token是否在置换保护期内
        //获取Token已经产生的时间 秒
        long pressd = Calendar.getInstance().getTimeInMillis() - createTime.getTime();
        if (pressd < REPLACETOKEN__PROTECTION_TIMEOUT * 1000) {
            throw new TokenValidationFailedException("Token处于保护期内，禁止替换,剩余保护时间" + (REPLACETOKEN__PROTECTION_TIMEOUT * 1000 - pressd) / 1000 + "秒");
        }
        //置换Token
        ItripUser itripUser = this.load(token);
        Long ttl = redisAPI.ttl(token);//剩余有效期
        if (ttl > 0 || ttl == -1) {//兼容手机和PC，手机端由于没有时间限制，所有这里返回的是-1
            String newToken = generateToken(userAgent, itripUser);
            this.save(newToken, itripUser);
            //两分钟后旧的Token失效
            redisAPI.set(token, this.REPLACETOKEN__DIE, JSON.toJSONString(itripUser));
            return newToken;
        } else {
            throw new TokenValidationFailedException("Token失效时间异常，禁止置换");
        }
    }

    /**
     * 通过加载用户
     *
     * @param token
     * @return
     */
    public ItripUser load(String token) {
        return JSON.parseObject(redisAPI.get("token"), ItripUser.class);
    }
}
